﻿using System;
using System.Collections.ObjectModel;
using System.Linq;
using SwiftMVVM.ViewModel;
using SwiftMVVM.ChangeTracking;
using System.ComponentModel;

namespace SampleApplication.Data
{
    /// <summary>
    /// The order manager handles all data and actions pertainable to
    /// orders.
    /// We're deriving off of DependentBindingObject because we'll be sending
    /// notifications to our child order objects when PricePerPound changes
    /// </summary>
    [Description("OrderManager")]
    public abstract class OrderManager : DependentBindingObject
    {
        public int ? OrderBatchID { get; set; }

        /// <summary>
        /// The change manager is responsible for all the bookeeping of all the changes in
        /// the object graph it's connected to. We only need to 
        /// create this object if the object will be getting edited.
        /// If the object were to be instantiated in -Readonly- or Reporting
        /// mode, then there is no need for the overhead of change tracking
        /// </summary>
        public ChangeManager ChangeManager { get; private set; }

        /// <summary>
        /// The orders collection we'll be exposing.
        /// We mark it as TrackChanges because we're interested
        /// in knowing when items are added and removed from the collection
        /// as well as if any of the items in the collection are changed.
        /// </summary>
        [TrackChanges]
        [NotificationProperty]
        public virtual ObservableCollection<Order> Orders { get; protected set; }

        /// <summary>
        /// Used to modify the price of each individual order.
        /// </summary>
        [TrackChanges]
        [NotificationProperty]
        public virtual PriceMultiplier PriceMultiplier { get; set; }

        /// <summary>
        /// Determines whether or not order weight adjustments are allowable at all.
        /// </summary>
        [NotificationProperty]
        public virtual bool LockOrderWeightAdjustments { get; set; }

        /// <summary>
        /// The total price we'll be charging. It needs to be updated any time
        /// an item is Added or Removed from the Orders collection OR
        /// when the price on any item in the orders collection is updated
        /// </summary>
        [TrackChanges]
        [DependsOn("Orders/Item", "Price")]
        [DependsOn("Orders", "Count")]
        public decimal TotalPrice
        {
            get { return this.Orders.Sum(order => order.Price); }
        }

        /// <summary>
        /// The price per pound, we'll also be tracking this here.
        /// We've also specified here that we'll be sending a notification 
        /// to all the orders in the Orders collection when the 
        /// PricePerPound is changed.
        /// (Appending a slash / iterates over each order)
        /// </summary>
        [TrackChanges]
        [NotificationProperty]
        public virtual decimal PricePerPound { get; set; }

        /// <summary>
        /// Instantiates a new order manager.
        /// If this object was to be used for reporting services, in other
        /// words a readonly context. You can skip the line that
        /// creates the ChangeManager. That would essentially put the object 
        /// into readonly mode without any overhead from the change manager.
        /// </summary>
        public OrderManager()
        {
            //Set default values
            PriceMultiplier = PriceMultiplier.Single;
            Orders = new ObservableCollection<Order>(
                Enumerable.Range(0, 100)
                .Select(index => CreateRandomNewOrder(index)));

            
            PricePerPound = 1.25m;
            this.ChangeManager = new ChangeManager(this);
            
            //We turn this on in the root object to enable DependentBindingObjects
            //to get notifications when other objects in it's heirarchy recieve 
            //propertychanges
            this.UseChangeMonitor = true;
        }

        public Order CreateRandomNewOrder(int? orderID = null)
        {
            var order = NotifyProxyTypeManager.Allocate<Order>(this, orderID,
                        Enum.GetName(typeof(OrderType), Utility.GetNext(0, 12)), /*random order name*/
                        Utility.GetNext(5, 10), /*random order weight*/
                        DateTime.Now.AddDays(-14).AddDays(Utility.GetNext(0, 14)),
                        (States)Utility.GetNext(0, 49) /*random order destination state*/);
           
            return order;
        }

        /// <summary>
        /// Creates a new proxy wrapped order manager
        /// </summary>
        /// <returns></returns>
        public static OrderManager Create()
        {
            return NotifyProxyTypeManager.Allocate<OrderManager>();
        }
    }
}
